#include "stdlib.hpp"
#include <purewell/rwlock.hpp>

namespace purewell
{

#define _xchg() ((pthread_rwlock_t*)(m_pData))
#define _xchgattr() ((pthread_rwlockattr_t*)(m_pData))

PWRWLock::PWRWLock(const PWRWLockAttribute* pattr)
{
#if defined(HAVE_POSIX_RWLOCK)
	pthread_rwlock_t* pthis(new pthread_rwlock_t);
	if ( NULL == pthis )
	{
		return;
	}

	do
	{
		if ( 0 != pthread_rwlock_init(pthis,
			(pattr?(pthread_rwlockattr_t*)(pattr->getData()):NULL)) )
		{
			break;
		}

		m_pData = pthis;
		return;
	} while (false);
#else
	m_pData = NULL;
	__PWERRNO_NOIMPL();
#endif
}

PWRWLock::~PWRWLock()
{
#if defined(HAVE_POSIX_RWLOCK)
	if ( NULL != m_pData )
	{
		pthread_rwlock_t* pthis((pthread_rwlock_t*)m_pData);
		delete pthis;
		m_pData = NULL;
	}
#else
	__PWERRNO_NOIMPL();
#endif
}

void
PWRWLock::lock(int type)
{
#if defined(HAVE_POSIX_RWLOCK)
	if ( RW_READ == type )
	{
		pthread_rwlock_rdlock(_xchg());
	}
	else
	{
		pthread_rwlock_wrlock(_xchg());
	}
#else
	__PWERRNO_NOIMPL();
#endif
}

bool
PWRWLock::lockTest(int type)
{
#if defined(HAVE_POSIX_RWLOCK)
	if ( RW_READ == type )
	{
		return 0 == pthread_rwlock_tryrdlock(_xchg());
	}
	else
	{
		return 0 == pthread_rwlock_trywrlock(_xchg());
	}
#else
	__PWERRNO_NOIMPL();
#endif
}

bool
PWRWLock::lockTimeLimit(int type, size_t msec)
{
#if defined(HAVE_POSIX_RWLOCK)
	struct timespec ts;
	ts.tv_sec = msec / 1000;
	ts.tv_nsec = (msec % 1000) * 1000;

	if ( RW_READ == type )
	{
		return 0 == pthread_rwlock_timedrdlock(_xchg(), &ts);
	}
	else
	{
		return 0 == pthread_rwlock_timedwrlock(_xchg(), &ts);
	}
#else
	__PWERRNO_NOIMPL();
#endif
}

void
PWRWLock::unlock(void)
{
#if defined(HAVE_POSIX_RWLOCK)
	pthread_rwlock_unlock(_xchg());
#else
	__PWERRNO_NOIMPL();
#endif
}

PWRWLock*
PWRWLock::create(const PWRWLockAttribute* pattr)
{
#if defined(HAVE_POSIX_RWLOCK)
	PWRWLock* lock(new PWRWLock(pattr));
	if ( NULL == lock )
	{
		return NULL;
	}

	if ( NULL == lock->m_pData )
	{
		delete lock;
		return NULL;
	}

	return lock;
#else
	__PWERRNO_NOIMPL();
	return NULL;
#endif
}

void
PWRWLock::release(PWRWLock* lock)
{
	delete lock;
}

PWRWLockAttribute::PWRWLockAttribute()
{
#if defined(HAVE_POSIX_RWLOCK)
	pthread_rwlockattr_t* pthis(new pthread_rwlockattr_t);
	if ( NULL == pthis )
	{
		return;
	}

	if ( 0 != pthread_rwlockattr_init(pthis) )
	{
		delete pthis;
		return;
	}

	m_pData = pthis;
#else
	__PWERRNO_NOIMPL();
	m_pData = NULL;
#endif
}

PWRWLockAttribute::~PWRWLockAttribute()
{
#if defined(HAVE_POSIX_RWLOCK)
	if ( NULL != m_pData )
	{
		pthread_rwlockattr_t* pthis((pthread_rwlockattr_t*)m_pData);
		delete pthis;
		m_pData = NULL;
	}
#else
	__PWERRNO_NOIMPL();
#endif
}

PWRWLockAttribute*
PWRWLockAttribute::create(void)
{
#if defined(HAVE_POSIX_RWLOCK)
	PWRWLockAttribute* pthis(new PWRWLockAttribute);
	if ( NULL == pthis )
	{
		return NULL;
	}

	if ( NULL == pthis->m_pData )
	{
		delete pthis;
		return NULL;
	}

	return pthis;
#else
	__PWERRNO_NOIMPL();
	return NULL;
#endif
}

void
PWRWLockAttribute::release(PWRWLockAttribute* obj)
{
	delete obj;
}

};//namespace purewell

